module("framework.core.eventsystem.LuaEventSystem",package.seeall)

---@class LuaEventSystem
local LuaEventSystem = {}

LuaEventSystem.Idle = 1
LuaEventSystem.Active = 2
LuaEventSystem.ToInsert = 3
LuaEventSystem.ToDelete = 4

LuaEventSystem.High = 1
LuaEventSystem.Common = 2
LuaEventSystem.Low = 3

--给某个类添加事件管理机制
function LuaEventSystem.addEventMechanism(luaObj)
    luaObj._allEvents = {}
    luaObj._allDeltaEvents = {}
    luaObj._inDispatching = {}
    luaObj._dispatchDelta = {}

    --注册一个事件回调
    luaObj.registerCallback = function(eventSender, eventName, callback, cbObj, priority)
        if nil == callback then
            logError("LuaEventSystem registerCallback callback shoule not be nil, please check it!")
            return
        end

        local toDelete = false
        --如果正在派发这个事件，则判定是否在修改列表中
        if eventSender._inDispatching[eventName] then
            local status = eventSender:_getStatusInDeltaList(eventName, callback, cbObj)
            if status == LuaEventSystem.ToDelete then
                toDelete = true
            elseif status == LuaEventSystem.ToInsert then   --已经在添加的缓存队列中了，则直接返回
                return
            end
        end

        local isInQueue = eventSender:_isInEventQueue(eventName, callback, cbObj)
        if isInQueue and not toDelete then
            return
        end

        local eventItem = EventItem.getPool():getObject()
        eventItem.eventName = eventName
        eventItem.callback = callback
        eventItem:setCbObj(cbObj)
        eventItem.priority = priority or LuaEventSystem.Common

        --如果正在派发事件，则直接加到缓存队列中，延迟添加；否则直接添加到事件列表中
        if eventSender._inDispatching[eventName] then
            eventSender:_removeFromDeltaList(eventName, callback, cbObj)
            eventItem.status = LuaEventSystem.ToInsert
            local deltaList = eventSender._allDeltaEvents[eventName]
            if not deltaList then
                deltaList = {}
                eventSender._allDeltaEvents[eventName] = deltaList
            end
            table.insert(deltaList, eventItem)
        else
            eventSender:_directAddEvent(eventItem)
        end
    end

    --取消一个事件回调
    luaObj.unregisterCallback = function(eventSender, eventName, callback, cbObj, removeAll)
        local eventList = eventSender._allEvents[eventName]
        if not eventList then
            return
        end
        --如果正在派发此事件，则从修改列表中删除此事件，同时再修改列表中插入一个删除的事件
        if eventSender._inDispatching[eventName] then
            eventSender:_removeFromDeltaList(eventName, callback, cbObj, removeAll)
            local eventItem = EventItem.getPool():getObject()
            eventItem.eventName = eventName
            eventItem.callback = callback
            eventItem:setCbObj(cbObj)
            eventItem.status = LuaEventSystem.ToDelete
            eventItem.removeAll = removeAll
            local deltaList = eventSender._allDeltaEvents[eventName]
            if not deltaList then
                deltaList = {}
                eventSender._allDeltaEvents[eventName] = deltaList
            end
            table.insert(deltaList, eventItem)
        else
            eventSender:_directRemoveEvent(eventName, callback, cbObj, removeAll)
        end
    end

    --移除某个事件的所有监听
    luaObj.unregisterAllCallback = function(eventSender, eventName)
        local eventList = eventSender._allEvents[eventName]
        if not eventList then
            return
        end

        local removeList = {}
        local priorityList = nil
        --统计所有的回调事件，然后再去一一移除
        for idx = LuaEventSystem.High, LuaEventSystem.Low do
            priorityList = eventList[idx]
            for idy = 1, #priorityList do
                local eventItem = priorityList[idy]
                local removeItem = EventItem.getPool():getObject()
                removeItem.callback = eventItem.callback
                removeItem:setCbObj(eventItem:getCbObj())
                table.insert(removeList, removeItem)
            end
        end
        --延后一一移除
        for _, removeItem in ipairs(removeList) do
            eventSender:unregisterCallback(eventName, removeItem.callback, removeItem:getCbObj() )
        end
    end

    --派发所有的消息
    luaObj.dispatchEvent = function(eventSender, eventName, ...)
        if nil == luaObj._allEvents or nil == luaObj._allEvents[eventName] then
            return
        end

        --如果正在派发这个事件，则缓存起来,等待后面递归派发
        if luaObj._inDispatching[eventName] then
            local dispatchItem = DispatchItem.getPool():getObject()
            dispatchItem.eventName = eventName

            local args = ...
            if nil == args then
                dispatchItem.eventArgs = nil
            else
                dispatchItem.eventArgs = {...}
            end
            table.insert(luaObj._dispatchDelta, dispatchItem)
            return
        end

        luaObj._inDispatching[eventName] = true
        eventSender:_directDispatch(eventName, ...)
        luaObj._inDispatching[eventName] = nil

        --如果有缓存的删除、添加事件，则执行之
        local deltaEvents = eventSender._allDeltaEvents[eventName]
        if nil ~= deltaEvents then
            local deltaCount = #deltaEvents
            local eventItem = nil
            for idx = 1, deltaCount do
                eventItem = deltaEvents[idx]
                if LuaEventSystem.ToInsert == eventItem.status then
                    eventSender:_directAddEvent(eventItem)
                elseif LuaEventSystem.ToDelete == eventItem.status then
                    eventSender:_directRemoveEvent(eventItem.eventName, eventItem.callback, eventItem:getCbObj(), eventItem.removeAll)
                    EventItem.getPool():putObject(eventItem)
                end
                deltaEvents[idx] = nil
            end
        end

        --如果有缓存的派发事件，则递归派发
        if #eventSender._dispatchDelta > 0 then
            local dispatchItem = table.remove(eventSender._dispatchDelta, 1)
            if not eventSender._inDispatching[dispatchItem.eventName] then
                if nil ~= dispatchItem.eventArgs then
                    eventSender:dispatchEvent(dispatchItem.eventName, unpack(dispatchItem.eventArgs))
                else
                    eventSender:dispatchEvent(dispatchItem.eventName)
                end
                DispatchItem.getPool():putObject(dispatchItem)
            else
                table.insert(eventSender._dispatchDelta, 1,dispatchItem)
            end
        end
    end

    --直接派发事件，不做其他考虑
    luaObj._directDispatch = function(eventSender, eventName, ...)
        local curEvents = luaObj._allEvents[eventName]
        local priorityItems = nil
        local priorityCount = nil
        local eventItem = nil
        for idx = LuaEventSystem.High, LuaEventSystem.Low do
            priorityItems = curEvents[idx]
            priorityCount = #priorityItems
            for idy = 1, priorityCount do
                eventItem = priorityItems[idy]
                if not eventItem:dispatch(...) then
                    eventSender:unregisterCallback(eventName, eventItem.callback, eventItem:getCbObj())
                end
            end
        end
    end

    --直接添加一个注册事件
    luaObj._directAddEvent = function(eventSender, eventItem)
        local curEventList = eventSender._allEvents[eventItem.eventName]
        --不存在这个事件的容器，则初始化之
        if nil == curEventList then
            curEventList = {}
            for idx = LuaEventSystem.High, LuaEventSystem.Low do
                curEventList[idx] = {}
            end
            eventSender._allEvents[eventItem.eventName] = curEventList
        end

        --如果已经在列表中了，则直接返回
        if eventSender:_isInEventQueue(eventItem.eventName, eventItem.callback, eventItem:getCbObj()) then
            EventItem.getPool():putObject(eventItem)
            return
        end

        --激活状态，添加到对应的优先级队列中
        eventItem.status = LuaEventSystem.Active
        table.insert(curEventList[eventItem.priority], eventItem)
    end

    --某个回调函数是否已经在事件队列中了
    luaObj._isInEventQueue = function(eventSender, eventName, callback, cbObj)
        local eventList = eventSender._allEvents[eventName]
        if nil == eventList then
            return false
        end

        local priorityList = nil
        for idx = LuaEventSystem.High, LuaEventSystem.Low do
            priorityList = eventList[idx]
            for idy = 1, #priorityList do
                local eventItem = priorityList[idy]
                if eventItem.callback == callback and eventItem:getCbObj() == cbObj then
                    return true
                end
            end
        end
        return false
    end

    --直接取消某个注册事件
    luaObj._directRemoveEvent = function(eventSender, eventName, callback, cbObj, removeAll)
        local eventList = eventSender._allEvents[eventName]
        if nil == eventList then
            return
        end

        local priorityList = nil
        for idx = LuaEventSystem.High, LuaEventSystem.Low do
            priorityList = eventList[idx]
            for idy = #priorityList, 1, -1 do
                local eventItem = priorityList[idy]
                if eventItem.callback == callback then
                    --如果是移除所有的，或者回调对象也一样，则移除此监听
                    if removeAll then
                        EventItem.getPool():putObject(eventItem)
                        table.remove(priorityList, idy)
                    elseif eventItem:getCbObj() == cbObj then
                        EventItem.getPool():putObject(eventItem)
                        table.remove(priorityList, idy)
                        break
                    end
                end
            end
        end
    end

    --查询某个注册回调在修改队列中的状态
    luaObj._getStatusInDeltaList = function(eventSender, eventName, callback, cbObj)
        local curDeltaList = eventSender._allDeltaEvents[eventName]
        if nil == curDeltaList then
            return 0
        end

        local deltaItem = nil
        for idx = #curDeltaList, 1, -1 do
            deltaItem = curDeltaList[idx]
            if deltaItem.eventName == eventName and deltaItem.callback == callback and deltaItem:getCbObj() == cbObj then
                return deltaItem.status
            end
        end
    end

    --从修改列表中删除某个注册回调
    luaObj._removeFromDeltaList = function (eventSender, eventName, callback, cbObj, removeAll)
        local deltaList = eventSender._allDeltaEvents[eventName]
        if not deltaList then
            return
        end

        local eventItem = nil
        for idx = #deltaList, 1, -1 do
            eventItem = deltaList[idx]
            if eventItem.callback == callback then
                if removeAll then
                    EventItem.getPool():putObject(eventItem)
                    table.remove(deltaList, idx)
                elseif eventItem:getCbObj() == cbObj then
                    EventItem.getPool():putObject(eventItem)
                    table.remove(deltaList, idx)
                    break
                end
            end
        end
    end
end

return LuaEventSystem
